suppressPackageStartupMessages(library(tidyverse))
library(gapminder)
Saving Graphs to File
- Don’t use the mouse
- Use
ggsave for ggplot
- Practice by saving the following plot to file:
ggplot(mtcars, aes(hp, wt)) +
geom_point()

#ggsave(FILENAME_HERE, PLOT_OBJECT_HERE)
- Base R way: print plots “to screen”, sandwiched between
pdf()/jpeg()/png()… and dev.off().
- Vector vs. raster: Images are stored on your computer as either vector or raster.
Scales; Colour
Scale functions in ggplot2 take the form scale_[aesthetic]_[mapping]().
Let’s first focus on the following plot:
p_scales <- ggplot(gapminder, aes(gdpPercap, lifeExp)) +
geom_point(aes(colour=pop), alpha=0.2)
p_scales +
scale_x_log10() +
scale_colour_continuous(trans="log10")

- Change the y-axis tick mark spacing to 10; change the colour spacing to include all powers of 10.
p_scales +
scale_x_log10() +
scale_colour_continuous(
trans = "log10",
breaks = 10^(1:10)
) +
scale_y_continuous(breaks=1:10 * 10) #vector of 1 to 10, multiplied by 10

#indicates what number we'd like to put tick marks for; if you're out of range, default is to exclude those tick marks
#continuous does not transform the axis at all
- Specify
scales::*_format in the labels argument of a scale function to do the following:
- Change the x-axis labels to dollar format (use
scales::dollar_format())
- Change the colour labels to comma format (use
scales::comma_format())
library(scales)
p_scales +
scale_x_log10(labels=dollar_format()) +
scale_colour_continuous(
trans = "log10",
breaks = 10^(1:10),
labels = comma_format()
) +
scale_y_continuous(breaks=10*(1:10))

- Use
RColorBrewer to change the colour scheme.
- Notice the three different types of scales: sequential, diverging, and continuous. For discrete or categorical variables, use very different colours. -> continuous For a sequence of variables, use a gradient of colours. -> sequential For a middle neutral value with a scale of gradients moving away in each direction (i.e. temperature scale to hot and cold), -> diverging
## All palettes the come with RColorBrewer:
RColorBrewer::display.brewer.all()

p_scales +
scale_x_log10(labels=dollar_format()) +
scale_colour_distiller( #another one is scale_colour_brewer (one is for continuous, other handles the other two types)
trans = "log10",
breaks = 10^(1:10),
labels = comma_format(),
palette = "Greens" #get the name from looking at RColorBrewer overview
) +
scale_y_continuous(breaks=10*(1:10))

- Use
viridis to change the colour to a colour-blind friendly scheme
- Hint: add
scale_colour_viridis_c (c stands for continuous; d discrete).
- You can choose a palette with
option.
p_scales +
scale_x_log10(labels=dollar_format()) +
scale_colour_viridis_c(
trans = "log10",
breaks = 10^(1:10),
labels = comma_format()
) +
scale_y_continuous(breaks=10*(1:10))

Theming
Changing the look of a graphic can be achieved through the theme() layer.
There are “complete themes” that come with ggplot2, my favourite being theme_bw (I’ve grown tired of the default gray background, so theme_bw is refreshing).
- Change the theme of the following plot to
theme_bw():
ggplot(iris, aes(Sepal.Width, Sepal.Length)) +
facet_wrap(~ Species) +
geom_point() +
labs(x = "Sepal Width",
y = "Sepal Length",
title = "Sepal sizes of three plant species") +
theme_bw() #many possibilities (see tab over)

- Then, change font size of axis labels, and the strip background colour. Others?
ggplot(iris, aes(Sepal.Width, Sepal.Length)) +
facet_wrap(~ Species) +
geom_point() +
labs(x = "Sepal Width",
y = "Sepal Length",
title = "Sepal sizes of three plant species") +
theme_bw() +
theme(axis.text = element_text(size = 16),
strip.background = element_rect(fill = "orange"),
panel.background = element_rect(fill = "blue"))

Plotly
Consider the following plot:
(p <- gapminder %>%
filter(continent != "Oceania") %>%
ggplot(aes(gdpPercap, lifeExp)) +
geom_point(aes(colour=pop), alpha=0.2) +
scale_x_log10(labels=dollar_format()) +
scale_colour_distiller(
trans = "log10",
breaks = 10^(1:10),
labels = comma_format(),
palette = "Greens"
) +
facet_wrap(~ continent) +
scale_y_continuous(breaks=10*(1:10)) +
theme_bw())

- Convert it to a
plotly object by applying the ggplotly() function:
#you can make a ggplot object and convert it to plotly (dont need to know plotly syntax)
#install.packages("plotly")
library(plotly)
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
ggplotly(p)
- You can save a plotly graph locally as an html file. Try saving the above:
- NOTE: plotly graphs don’t seem to show up in Rmd notebooks, but they do with Rmd documents.
p %>%
ggplotly() %>%
htmlwidgets::saveWidget("LOCATION_GOES_HERE")
- Run this code to see the json format underneath:
p %>%
ggplotly() %>%
plotly_json()
- Check out code to make a plotly object from scratch using
plot_ly() – scatterplot of gdpPercap vs lifeExp.
plot_ly(gapminder,
x = ~gdpPercap, #tilda means make a formula with this variable as your formula
y = ~lifeExp,
type = "scatter",
mode = "markers",
opacity = 0.2) %>%
layout(xaxis = list(type = "log"))
- Add population to form a z-axis for a 3D plot:
plot_ly(gapminder,
x = ~gdpPercap,
y = ~lifeExp,
z = ~pop,
type = "scatter3d",
mode = "markers",
opacity = 0.2)
can use gg animate function to animate plots
Why do we make plots? EDA (exploratory data analysis); drawing conclusions from plots -> endpoint - we’ve looked at tooling with plots -> intermediate - grammar and theme are the two aspects that we make decisions about when producing a plot -> intermediate - effectiveness; what information is contained in the data? -> starting point - see jenny’s animated gif for simplfying data -> does it facilitate comparisons or reveal trends? - see Tamara Munsener’s book for psychology of colour effects on perception - don’t use pie charts! can be difficult to compare categories (hard to perceive angular distances vs linear, as in a bar plot) - when you look at your plot, ask yourself what trends can you see; what is hidden; how can you reveal it? - impossible to make a perfect plot (this is why we have figure captions) - publication quality: - no colour - resolution/aspect ratio/size - font size - sans serif - labels, units - consistency - inluding zero is important for bar charts (e.g.) bc we need to visually gauge the entire area of the bar for comparison
LS0tDQp0aXRsZTogImNtMDEzIEV4ZXJjaXNlIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7cn0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpDQpsaWJyYXJ5KGdhcG1pbmRlcikNCmBgYA0KDQoNCiMgU2F2aW5nIEdyYXBocyB0byBGaWxlDQoNCi0gRG9uJ3QgdXNlIHRoZSBtb3VzZQ0KLSBVc2UgYGdnc2F2ZWAgZm9yIGdncGxvdA0KICAgIC0gUHJhY3RpY2UgYnkgc2F2aW5nIHRoZSBmb2xsb3dpbmcgcGxvdCB0byBmaWxlOiANCg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoaHAsIHd0KSkgKyANCiAgICBnZW9tX3BvaW50KCkNCiNnZ3NhdmUoRklMRU5BTUVfSEVSRSwgUExPVF9PQkpFQ1RfSEVSRSkNCmBgYA0KDQotIEJhc2UgUiB3YXk6IHByaW50IHBsb3RzICJ0byBzY3JlZW4iLCBzYW5kd2ljaGVkIGJldHdlZW4gYHBkZigpYC9ganBlZygpYC9gcG5nKClgLi4uIGFuZCBgZGV2Lm9mZigpYC4gDQotIFZlY3RvciB2cy4gcmFzdGVyOiBJbWFnZXMgYXJlIHN0b3JlZCBvbiB5b3VyIGNvbXB1dGVyIGFzIGVpdGhlciBfdmVjdG9yXyBvciBfcmFzdGVyXy4NCiAgICAtIF9fUmFzdGVyX186IGFuIGBuYCBieSBgbWAgZ3JpZCBvZiBwaXhlbHMsIGVhY2ggd2l0aCBpdHMgb3duIGNvbG91ci4gYGpwZWdgLCBgcG5nYCwgYGdpZmAsIGBibXBgLg0KICAgIC0gX19WZWN0b3JfXzogcmVwcmVzZW50ZWQgYXMgc2hhcGVzIGFuZCBsaW5lcy4gYHBkZmAsIFtgc3ZnYF0oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9ncmFwaGljcy9zdmdfaW50cm8uYXNwKS4NCiAgICAtIEZvciB0aXBzOiBbIjEwIHRpcHMgZm9yIG1ha2luZyB5b3VyIFIgZ3JhcGhpY3MgbG9vayB0aGVpciBiZXN0IiJdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMDkvMDEvMTAtdGlwcy1mb3ItbWFraW5nLXlvdXItci1ncmFwaGljcy1sb29rLXRoZWlyLWJlc3QuaHRtbCkuDQogICAgDQojIFNjYWxlczsgQ29sb3VyDQoNClNjYWxlIGZ1bmN0aW9ucyBpbiBgZ2dwbG90MmAgdGFrZSB0aGUgZm9ybSBgc2NhbGVfW2Flc3RoZXRpY11fW21hcHBpbmddKClgLg0KDQpMZXQncyBmaXJzdCBmb2N1cyBvbiB0aGUgZm9sbG93aW5nIHBsb3Q6DQoNCmBgYHtyfQ0KcF9zY2FsZXMgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCkpICsNCiAgICAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXBvcCksIGFscGhhPTAuMikNCnBfc2NhbGVzICsgDQogICAgc2NhbGVfeF9sb2cxMCgpICsNCiAgICBzY2FsZV9jb2xvdXJfY29udGludW91cyh0cmFucz0ibG9nMTAiKQ0KYGBgDQoNCjEuIENoYW5nZSB0aGUgeS1heGlzIHRpY2sgbWFyayBzcGFjaW5nIHRvIDEwOyBjaGFuZ2UgdGhlIGNvbG91ciBzcGFjaW5nIHRvIGluY2x1ZGUgYWxsIHBvd2VycyBvZiAxMC4NCg0KYGBge3J9DQpwX3NjYWxlcyArDQogICAgc2NhbGVfeF9sb2cxMCgpICsNCiAgICBzY2FsZV9jb2xvdXJfY29udGludW91cygNCiAgICAgICAgdHJhbnMgID0gImxvZzEwIiwgDQogICAgICAgIGJyZWFrcyA9IDEwXigxOjEwKQ0KICAgICkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3M9MToxMCAqIDEwKSAjdmVjdG9yIG9mIDEgdG8gMTAsIG11bHRpcGxpZWQgYnkgMTAgDQojaW5kaWNhdGVzIHdoYXQgbnVtYmVyIHdlJ2QgbGlrZSB0byBwdXQgdGljayBtYXJrcyBmb3I7IGlmIHlvdSdyZSBvdXQgb2YgcmFuZ2UsIGRlZmF1bHQgaXMgdG8gZXhjbHVkZSB0aG9zZSB0aWNrIG1hcmtzDQojY29udGludW91cyBkb2VzIG5vdCB0cmFuc2Zvcm0gdGhlIGF4aXMgYXQgYWxsDQpgYGANCg0KMi4gU3BlY2lmeSBgc2NhbGVzOjoqX2Zvcm1hdGAgaW4gdGhlIGBsYWJlbHNgIGFyZ3VtZW50IG9mIGEgc2NhbGUgZnVuY3Rpb24gdG8gZG8gdGhlIGZvbGxvd2luZzoNCiAgICAtIENoYW5nZSB0aGUgeC1heGlzIGxhYmVscyB0byBkb2xsYXIgZm9ybWF0ICh1c2UgYHNjYWxlczo6ZG9sbGFyX2Zvcm1hdCgpYCkNCiAgICAtIENoYW5nZSB0aGUgY29sb3VyIGxhYmVscyB0byBjb21tYSBmb3JtYXQgKHVzZSBgc2NhbGVzOjpjb21tYV9mb3JtYXQoKWApDQoNCmBgYHtyfQ0KbGlicmFyeShzY2FsZXMpDQpwX3NjYWxlcyArDQogICAgc2NhbGVfeF9sb2cxMChsYWJlbHM9ZG9sbGFyX2Zvcm1hdCgpKSArDQogICAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMoDQogICAgICAgIHRyYW5zICA9ICJsb2cxMCIsIA0KICAgICAgICBicmVha3MgPSAxMF4oMToxMCksDQogICAgICAgIGxhYmVscyA9IGNvbW1hX2Zvcm1hdCgpDQogICAgKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz0xMCooMToxMCkpDQpgYGANCg0KMy4gVXNlIGBSQ29sb3JCcmV3ZXJgIHRvIGNoYW5nZSB0aGUgY29sb3VyIHNjaGVtZS4NCiAgICAtIE5vdGljZSB0aGUgdGhyZWUgZGlmZmVyZW50IHR5cGVzIG9mIHNjYWxlczogc2VxdWVudGlhbCwgZGl2ZXJnaW5nLCBhbmQgY29udGludW91cy4NCiAgICBGb3IgZGlzY3JldGUgb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB1c2UgdmVyeSBkaWZmZXJlbnQgY29sb3Vycy4gLT4gY29udGludW91cw0KICAgIEZvciBhIHNlcXVlbmNlIG9mIHZhcmlhYmxlcywgdXNlIGEgZ3JhZGllbnQgb2YgY29sb3Vycy4gLT4gc2VxdWVudGlhbA0KICAgIEZvciBhIG1pZGRsZSBuZXV0cmFsIHZhbHVlIHdpdGggYSBzY2FsZSBvZiBncmFkaWVudHMgbW92aW5nIGF3YXkgaW4gZWFjaCBkaXJlY3Rpb24gKGkuZS4gdGVtcGVyYXR1cmUgc2NhbGUgdG8gaG90IGFuZCBjb2xkKSwgLT4gZGl2ZXJnaW5nDQoNCmBgYHtyfQ0KIyMgQWxsIHBhbGV0dGVzIHRoZSBjb21lIHdpdGggUkNvbG9yQnJld2VyOg0KUkNvbG9yQnJld2VyOjpkaXNwbGF5LmJyZXdlci5hbGwoKQ0KcF9zY2FsZXMgKw0KICAgIHNjYWxlX3hfbG9nMTAobGFiZWxzPWRvbGxhcl9mb3JtYXQoKSkgKw0KICAgIHNjYWxlX2NvbG91cl9kaXN0aWxsZXIoICAjYW5vdGhlciBvbmUgaXMgc2NhbGVfY29sb3VyX2JyZXdlciAob25lIGlzIGZvciBjb250aW51b3VzLCBvdGhlciBoYW5kbGVzIHRoZSBvdGhlciB0d28gdHlwZXMpDQogICAgICAgIHRyYW5zICAgPSAibG9nMTAiLA0KICAgICAgICBicmVha3MgID0gMTBeKDE6MTApLA0KICAgICAgICBsYWJlbHMgID0gY29tbWFfZm9ybWF0KCksDQogICAgICAgIHBhbGV0dGUgPSAiR3JlZW5zIiAjZ2V0IHRoZSBuYW1lIGZyb20gbG9va2luZyBhdCBSQ29sb3JCcmV3ZXIgb3ZlcnZpZXcNCiAgICApICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPTEwKigxOjEwKSkNCmBgYA0KDQo0LiBVc2UgYHZpcmlkaXNgIHRvIGNoYW5nZSB0aGUgY29sb3VyIHRvIGEgY29sb3VyLWJsaW5kIGZyaWVuZGx5IHNjaGVtZQ0KICAgIC0gSGludDogYWRkIGBzY2FsZV9jb2xvdXJfdmlyaWRpc19jYCAoYGNgIHN0YW5kcyBmb3IgY29udGludW91czsgYGRgIGRpc2NyZXRlKS4NCiAgICAtIFlvdSBjYW4gY2hvb3NlIGEgcGFsZXR0ZSB3aXRoIGBvcHRpb25gLg0KDQpgYGB7cn0NCnBfc2NhbGVzICsNCiAgICBzY2FsZV94X2xvZzEwKGxhYmVscz1kb2xsYXJfZm9ybWF0KCkpICsNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKA0KICAgICAgICB0cmFucyAgID0gImxvZzEwIiwNCiAgICAgICAgYnJlYWtzICA9IDEwXigxOjEwKSwNCiAgICAgICAgbGFiZWxzICA9IGNvbW1hX2Zvcm1hdCgpDQogICAgKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz0xMCooMToxMCkpDQpgYGANCg0KIyBUaGVtaW5nDQoNCkNoYW5naW5nIHRoZSBsb29rIG9mIGEgZ3JhcGhpYyBjYW4gYmUgYWNoaWV2ZWQgdGhyb3VnaCB0aGUgYHRoZW1lKClgIGxheWVyLg0KDQpUaGVyZSBhcmUgWyJjb21wbGV0ZSB0aGVtZXMiXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwpIHRoYXQgY29tZSB3aXRoIGBnZ3Bsb3QyYCwgbXkgZmF2b3VyaXRlIGJlaW5nIGB0aGVtZV9id2AgKEkndmUgZ3Jvd24gdGlyZWQgb2YgdGhlIGRlZmF1bHQgZ3JheSBiYWNrZ3JvdW5kLCBzbyBgdGhlbWVfYndgIGlzIHJlZnJlc2hpbmcpLg0KDQoxLiBDaGFuZ2UgdGhlIHRoZW1lIG9mIHRoZSBmb2xsb3dpbmcgcGxvdCB0byBgdGhlbWVfYncoKWA6DQoNCmBgYHtyfQ0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5XaWR0aCwgU2VwYWwuTGVuZ3RoKSkgKw0KICAgICBmYWNldF93cmFwKH4gU3BlY2llcykgKw0KICAgICBnZW9tX3BvaW50KCkgKw0KICAgICBsYWJzKHggPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgIHkgPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICB0aXRsZSA9ICJTZXBhbCBzaXplcyBvZiB0aHJlZSBwbGFudCBzcGVjaWVzIikgKw0KICAgIHRoZW1lX2J3KCkgI21hbnkgcG9zc2liaWxpdGllcyAoc2VlIHRhYiBvdmVyKQ0KYGBgDQoNCjIuIFRoZW4sIGNoYW5nZSBmb250IHNpemUgb2YgYXhpcyBsYWJlbHMsIGFuZCB0aGUgc3RyaXAgYmFja2dyb3VuZCBjb2xvdXIuIE90aGVycz8NCg0KYGBge3J9DQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLldpZHRoLCBTZXBhbC5MZW5ndGgpKSArDQogICAgIGZhY2V0X3dyYXAofiBTcGVjaWVzKSArDQogICAgIGdlb21fcG9pbnQoKSArDQogICAgIGxhYnMoeCA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgeSA9ICJTZXBhbCBMZW5ndGgiLA0KICAgICAgICAgIHRpdGxlID0gIlNlcGFsIHNpemVzIG9mIHRocmVlIHBsYW50IHNwZWNpZXMiKSArDQogICAgdGhlbWVfYncoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksDQogICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIm9yYW5nZSIpLA0KICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJibHVlIikpDQoNCiNub3RlOiB0aGVtZSBlbGVtZW50cyBhcmUgbm90IHBhcnQgb2YgdGhlIGdyYW1tZXIgKHBsb3QgZWxlbWVudHMgYXJlIG5vdCBmdW5kYW1lbnRhbGx5IGNoYW5nZWQpDQpgYGANCg0KDQojIFBsb3RseQ0KDQpDb25zaWRlciB0aGUgZm9sbG93aW5nIHBsb3Q6DQoNCmBgYHtyfQ0KKHAgPC0gZ2FwbWluZGVyICU+JSANCiAgICAgZmlsdGVyKGNvbnRpbmVudCAhPSAiT2NlYW5pYSIpICU+JSANCiAgICAgZ2dwbG90KGFlcyhnZHBQZXJjYXAsIGxpZmVFeHApKSArDQogICAgIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wb3ApLCBhbHBoYT0wLjIpICsNCiAgICAgc2NhbGVfeF9sb2cxMChsYWJlbHM9ZG9sbGFyX2Zvcm1hdCgpKSArDQogICAgIHNjYWxlX2NvbG91cl9kaXN0aWxsZXIoDQogICAgICAgICB0cmFucyAgID0gImxvZzEwIiwNCiAgICAgICAgIGJyZWFrcyAgPSAxMF4oMToxMCksDQogICAgICAgICBsYWJlbHMgID0gY29tbWFfZm9ybWF0KCksDQogICAgICAgICBwYWxldHRlID0gIkdyZWVucyINCiAgICAgKSArDQogICAgIGZhY2V0X3dyYXAofiBjb250aW5lbnQpICsNCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz0xMCooMToxMCkpICsNCiAgICAgdGhlbWVfYncoKSkNCmBgYA0KDQoxLiBDb252ZXJ0IGl0IHRvIGEgYHBsb3RseWAgb2JqZWN0IGJ5IGFwcGx5aW5nIHRoZSBgZ2dwbG90bHkoKWAgZnVuY3Rpb246DQoNCmBgYHtyfQ0KI3lvdSBjYW4gbWFrZSBhIGdncGxvdCBvYmplY3QgYW5kIGNvbnZlcnQgaXQgdG8gcGxvdGx5IChkb250IG5lZWQgdG8ga25vdyBwbG90bHkgc3ludGF4KQ0KI2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQoNCmxpYnJhcnkocGxvdGx5KQ0KZ2dwbG90bHkocCkNCmBgYA0KDQoyLiBZb3UgY2FuIHNhdmUgYSBwbG90bHkgZ3JhcGggbG9jYWxseSBhcyBhbiBodG1sIGZpbGUuIFRyeSBzYXZpbmcgdGhlIGFib3ZlOg0KICAgIC0gTk9URTogcGxvdGx5IGdyYXBocyBkb24ndCBzZWVtIHRvIHNob3cgdXAgaW4gUm1kIF9ub3RlYm9va3NfLCBidXQgdGhleSBkbyB3aXRoIFJtZCBfZG9jdW1lbnRzXy4NCg0KYGBge3J9DQpwICU+JSANCiAgICBnZ3Bsb3RseSgpICU+JSANCiAgICBodG1sd2lkZ2V0czo6c2F2ZVdpZGdldCgiTE9DQVRJT05fR09FU19IRVJFIikNCmBgYA0KDQoNCjMuIFJ1biB0aGlzIGNvZGUgdG8gc2VlIHRoZSBqc29uIGZvcm1hdCB1bmRlcm5lYXRoOg0KDQpgYGB7cn0NCnAgJT4lIA0KICAgIGdncGxvdGx5KCkgJT4lIA0KICAgIHBsb3RseV9qc29uKCkgI3Bsb3QgaXMganVzdCBwbGFpbiB0ZXh0DQpgYGANCg0KDQo0LiBDaGVjayBvdXQgY29kZSB0byBtYWtlIGEgcGxvdGx5IG9iamVjdCBmcm9tIHNjcmF0Y2ggdXNpbmcgYHBsb3RfbHkoKWAgLS0gc2NhdHRlcnBsb3Qgb2YgZ2RwUGVyY2FwIHZzIGxpZmVFeHAuDQogICAgLSBDaGVjayBvdXQgdGhlIFtjaGVhdCBzaGVldF0oaHR0cHM6Ly9pbWFnZXMucGxvdC5seS9wbG90bHktZG9jdW1lbnRhdGlvbi9pbWFnZXMvcl9jaGVhdF9zaGVldC5wZGYpLg0KDQpgYGB7cn0NCnBsb3RfbHkoZ2FwbWluZGVyLCANCiAgICAgICAgeCA9IH5nZHBQZXJjYXAsICN0aWxkYSBtZWFucyBtYWtlIGEgZm9ybXVsYSB3aXRoIHRoaXMgdmFyaWFibGUgYXMgeW91ciBmb3JtdWxhDQogICAgICAgIHkgPSB+bGlmZUV4cCwgDQogICAgICAgIHR5cGUgPSAic2NhdHRlciIsDQogICAgICAgIG1vZGUgPSAibWFya2VycyIsDQogICAgICAgIG9wYWNpdHkgPSAwLjIpICU+JSANCiAgICBsYXlvdXQoeGF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIikpDQpgYGANCg0KNS4gQWRkIHBvcHVsYXRpb24gdG8gZm9ybSBhIHotYXhpcyBmb3IgYSAzRCBwbG90Og0KDQpgYGB7cn0NCnBsb3RfbHkoZ2FwbWluZGVyLCANCiAgICAgICAgeCA9IH5nZHBQZXJjYXAsIA0KICAgICAgICB5ID0gfmxpZmVFeHAsIA0KICAgICAgICB6ID0gfnBvcCwNCiAgICAgICAgdHlwZSA9ICJzY2F0dGVyM2QiLA0KICAgICAgICBtb2RlID0gIm1hcmtlcnMiLA0KICAgICAgICBvcGFjaXR5ID0gMC4yKQ0KYGBgDQoNCiNjYW4gdXNlIGdnIGFuaW1hdGUgZnVuY3Rpb24gdG8gYW5pbWF0ZSBwbG90cw0KDQpXaHkgZG8gd2UgbWFrZSBwbG90cz8gRURBIChleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzKTsgZHJhd2luZyBjb25jbHVzaW9ucyBmcm9tIHBsb3RzIC0+IGVuZHBvaW50DQotIHdlJ3ZlIGxvb2tlZCBhdCB0b29saW5nIHdpdGggcGxvdHMgLT4gaW50ZXJtZWRpYXRlDQotIGdyYW1tYXIgYW5kIHRoZW1lIGFyZSB0aGUgdHdvIGFzcGVjdHMgdGhhdCB3ZSBtYWtlIGRlY2lzaW9ucyBhYm91dCB3aGVuIHByb2R1Y2luZyBhIHBsb3QgLT4gaW50ZXJtZWRpYXRlDQotIGVmZmVjdGl2ZW5lc3M7IHdoYXQgaW5mb3JtYXRpb24gaXMgY29udGFpbmVkIGluIHRoZSBkYXRhPyAtPiBzdGFydGluZyBwb2ludA0KLSBzZWUgamVubnkncyBhbmltYXRlZCBnaWYgZm9yIHNpbXBsZnlpbmcgZGF0YSAtPiBkb2VzIGl0IGZhY2lsaXRhdGUgY29tcGFyaXNvbnMgb3IgcmV2ZWFsIHRyZW5kcz8NCi0gc2VlIFRhbWFyYSBNdW5zZW5lcidzIGJvb2sgZm9yIHBzeWNob2xvZ3kgb2YgY29sb3VyIGVmZmVjdHMgb24gcGVyY2VwdGlvbg0KLSBkb24ndCB1c2UgcGllIGNoYXJ0cyEgY2FuIGJlIGRpZmZpY3VsdCB0byBjb21wYXJlIGNhdGVnb3JpZXMgKGhhcmQgdG8gcGVyY2VpdmUgYW5ndWxhciBkaXN0YW5jZXMgdnMgbGluZWFyLCBhcyBpbiBhIGJhciBwbG90KQ0KLSB3aGVuIHlvdSBsb29rIGF0IHlvdXIgcGxvdCwgYXNrIHlvdXJzZWxmIHdoYXQgdHJlbmRzIGNhbiB5b3Ugc2VlOyB3aGF0IGlzIGhpZGRlbjsgaG93IGNhbiB5b3UgcmV2ZWFsIGl0Pw0KLSBpbXBvc3NpYmxlIHRvIG1ha2UgYSBwZXJmZWN0IHBsb3QgKHRoaXMgaXMgd2h5IHdlIGhhdmUgZmlndXJlIGNhcHRpb25zKQ0KLSBwdWJsaWNhdGlvbiBxdWFsaXR5Og0KICAtIG5vIGNvbG91cg0KICAtIHJlc29sdXRpb24vYXNwZWN0IHJhdGlvL3NpemUNCiAgLSBmb250IHNpemUNCiAgLSBzYW5zIHNlcmlmDQogIC0gbGFiZWxzLCB1bml0cw0KICAtIGNvbnNpc3RlbmN5DQotIGlubHVkaW5nIHplcm8gaXMgaW1wb3J0YW50IGZvciBiYXIgY2hhcnRzIChlLmcuKSBiYyB3ZSBuZWVkIHRvIHZpc3VhbGx5IGdhdWdlIHRoZSBlbnRpcmUgYXJlYSBvZiB0aGUgYmFyIGZvciBjb21wYXJpc29uDQoNCg0K